package jp.reflexworks.atom.entry;

import java.io.Serializable;
import java.util.List;
import java.util.ArrayList;

import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.NotPersistent;
import javax.jdo.annotations.PrimaryKey;

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;

import jp.reflexworks.atom.source.Source;

@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Entry implements Serializable {
	
	@NotPersistent
	public static final String KIND = "Entry";

	@PrimaryKey
	@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
	// 親のURI＋子のURI＋revision
	private Key key;

	@Persistent
	// 更新のつどカウントUp
	private int revision;

	@Persistent
	// 日時が登録されているとtrue(削除済)、nullだとfalse(有効レコード)
	private String deleted;

	@Persistent
	// aliasレコードかどうか　true:alias、false:aliasでない
	private boolean isAlias;

	@Persistent
	// 自分までのURI
	private String parent;

	@Persistent
	// 自分のURI
	private String selfid;

	@Persistent
	public String _$xmlns;

	@Persistent
	public String _$xml$lang;

	@Persistent
	public String _$xml$base;

	@NotPersistent
	// 更新者
	public List<Author> author;

	@NotPersistent
	public List<Category> category;

	@NotPersistent
	// 説明（Text型）
	public Content content;

	@NotPersistent
	public List<Contributor> contributor;

	@Persistent
	// ユニークな文字列（Encodedkey)
	public String id;

	@Persistent
	public String id_$xml$lang;

	@Persistent
	public String id_$xml$base;

	@NotPersistent
	// 親＋自分のURIの値(属性rel="alternate"の、属性hrefに設定。)
	// タグやカテゴリページに組み込むページなど
	public List<Link> link;

	@Persistent
	public String published;

	@Persistent
	public String published_$xml$lang;

	@Persistent
	public String published_$xml$base;

	@Persistent
	public String rights;

	@Persistent
	public String rights_$type;

	@Persistent
	public String rights_$xml$lang;

	@Persistent
	public String rights_$xml$base;

	@NotPersistent
	public Source source;

	@Persistent
	public String summary;

	@Persistent
	public String summary_$type;

	@Persistent
	public String summary_$xml$lang;

	@Persistent
	public String summary_$xml$base;

	@Persistent
	// 表示文言（String型）
	public String title;

	@Persistent
	public String title_$type;

	@Persistent
	public String title_$xml$lang;

	@Persistent
	public String title_$xml$base;

	@Persistent
	// 表示文言（String型）
	public String subtitle;

	@Persistent
	public String subtitle_$type;

	@Persistent
	public String subtitle_$xml$lang;

	@Persistent
	public String subtitle_$xml$base;

	@Persistent
	// 更新日付（作成日付はrevision1のmodified)
	public String updated;

	@Persistent
	public String updated_$xml$lang;

	@Persistent
	public String updated_$xml$base;

	public Key getKey() {
		return key;
	}

	public void setKey(Key key) {
		this.key = key;
	}

	public int getRevision() {
		return revision;
	}

	public void setRevision(int revision) {
		this.revision = revision;
	}

	public String getDeleted() {
		return deleted;
	}

	public void setDeleted(String deleted) {
		this.deleted = deleted;
	}
	
	public boolean isDeleted() {
		if (!"".equals(deleted)) {
			return true;
		}
		return false;
	}

	public boolean isAlias() {
		return isAlias;
	}

	public void setAlias(boolean isAlias) {
		this.isAlias = isAlias;
	}

	public String getParent() {
		return parent;
	}

	public void setParent(String parent) {
		if (parent != null) {
			if (!"/".equals(parent.substring(parent.length() - 1))) {
				this.parent = parent + "/";
			} else {
				this.parent = parent;
			}
		}
	}

	public String getSelfid() {
		return selfid;
	}

	public void setSelfid(String selfid) {
		if (selfid != null) {
			if ("/".equals(selfid)) {
				this.selfid = selfid;	// root layer
			} else if (selfid.length() > 0 && "/".equals(selfid.substring(selfid.length() - 1))) {
				this.selfid = selfid.substring(0, selfid.length() - 1);
			} else {
				this.selfid = selfid;
			}
		}
	}

	public String get_$xmlns() {
		return _$xmlns;
	}

	public void set_$xmlns(String _$xmlns) {
		this._$xmlns = _$xmlns;
	}

	public String get_$xml$lang() {
		return _$xml$lang;
	}

	public void set_$xml$lang(String _$xml$lang) {
		this._$xml$lang = _$xml$lang;
	}

	public String get_$xml$base() {
		return _$xml$base;
	}

	public void set_$xml$base(String _$xml$base) {
		this._$xml$base = _$xml$base;
	}

	public List<Author> getAuthor() {
		return author;
	}

	public void setAuthor(List<Author> author) {
		this.author = author;
	}

	public List<Category> getCategory() {
		return category;
	}

	public void setCategory(List<Category> category) {
		this.category = category;
	}

	public Content getContent() {
		return content;
	}

	public void setContent(Content content) {
		this.content = content;
	}

	public List<Contributor> getContributor() {
		return contributor;
	}

	public void setContributor(List<Contributor> contributor) {
		this.contributor = contributor;
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getId_$xml$lang() {
		return id_$xml$lang;
	}

	public void setId_$xml$lang(String id_$xml$lang) {
		this.id_$xml$lang = id_$xml$lang;
	}

	public String getId_$xml$base() {
		return id_$xml$base;
	}

	public void setId_$xml$base(String id_$xml$base) {
		this.id_$xml$base = id_$xml$base;
	}

	public List<Link> getLink() {
		return link;
	}

	public void setLink(List<Link> link) {
		this.link = link;
	}

	public String getPublished() {
		return published;
	}

	public void setPublished(String published) {
		this.published = published;
	}

	public String getPublished_$xml$lang() {
		return published_$xml$lang;
	}

	public void setPublished_$xml$lang(String published_$xml$lang) {
		this.published_$xml$lang = published_$xml$lang;
	}

	public String getPublished_$xml$base() {
		return published_$xml$base;
	}

	public void setPublished_$xml$base(String published_$xml$base) {
		this.published_$xml$base = published_$xml$base;
	}

	public String getRights() {
		return rights;
	}

	public void setRights(String rights) {
		this.rights = rights;
	}

	public String getRights_$type() {
		return rights_$type;
	}

	public void setRights_$type(String rights_$type) {
		this.rights_$type = rights_$type;
	}

	public String getRights_$xml$lang() {
		return rights_$xml$lang;
	}

	public void setRights_$xml$lang(String rights_$xml$lang) {
		this.rights_$xml$lang = rights_$xml$lang;
	}

	public String getRights_$xml$base() {
		return rights_$xml$base;
	}

	public void setRights_$xml$base(String rights_$xml$base) {
		this.rights_$xml$base = rights_$xml$base;
	}

	public Source getSource() {
		return source;
	}

	public void setSource(Source source) {
		this.source = source;
	}

	public String getSummary() {
		return summary;
	}

	public void setSummary(String summary) {
		this.summary = summary;
	}

	public String getSummary_$type() {
		return summary_$type;
	}

	public void setSummary_$type(String summary_$type) {
		this.summary_$type = summary_$type;
	}

	public String getSummary_$xml$lang() {
		return summary_$xml$lang;
	}

	public void setSummary_$xml$lang(String summary_$xml$lang) {
		this.summary_$xml$lang = summary_$xml$lang;
	}

	public String getSummary_$xml$base() {
		return summary_$xml$base;
	}

	public void setSummary_$xml$base(String summary_$xml$base) {
		this.summary_$xml$base = summary_$xml$base;
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public String getTitle_$type() {
		return title_$type;
	}

	public void setTitle_$type(String title_$type) {
		this.title_$type = title_$type;
	}

	public String getTitle_$xml$lang() {
		return title_$xml$lang;
	}

	public void setTitle_$xml$lang(String title_$xml$lang) {
		this.title_$xml$lang = title_$xml$lang;
	}

	public String getTitle_$xml$base() {
		return title_$xml$base;
	}

	public void setTitle_$xml$base(String title_$xml$base) {
		this.title_$xml$base = title_$xml$base;
	}

	public String getUpdated() {
		return updated;
	}

	public void setUpdated(String updated) {
		this.updated = updated;
	}

	public String getUpdated_$xml$lang() {
		return updated_$xml$lang;
	}

	public void setUpdated_$xml$lang(String updated_$xml$lang) {
		this.updated_$xml$lang = updated_$xml$lang;
	}

	public String getUpdated_$xml$base() {
		return updated_$xml$base;
	}

	public void setUpdated_$xml$base(String updated_$xml$base) {
		this.updated_$xml$base = updated_$xml$base;
	}

	/**
	 * 設定された値よりkeyの文字列を返却
	 * @return
	 */
	public String getOriginalKeyStr() {
		// 親のURI＋子のURI＋revision
		StringBuffer originalKeyStr = new StringBuffer();
		originalKeyStr.append(getMyUri());
		originalKeyStr.append(",");
		originalKeyStr.append(revision);
		return originalKeyStr.toString();
	}
	
	/**
	 * 親キーを返却
	 * @return
	 */
	public Key getParentKey() {
		return getParentKey(getMyUri());
	}
	
	/**
	 * 親キーを返却
	 * @return
	 */
	public static Key getParentKey(String uri) {
		Key parentKey = KeyFactory.createKey(Entry.KIND, uri);
		return parentKey;
	}
	
	/**
	 * currentレコードのキーを返却
	 * @return
	 */
	public Key getCurrentKey() {
		return getCurrentKey(getMyUri());
	}
	
	/**
	 * currentレコードのキーを返却
	 * @return
	 */
	public static Key getCurrentKey(String uri) {
		StringBuffer keyStr = new StringBuffer();
		keyStr.append(uri);
		keyStr.append(",0");	// currentキーはrevision=0
		Key parentKey = getParentKey(uri);
		Key key = KeyFactory.createKey(parentKey, Entry.KIND, keyStr.toString());
		return key;
	}

	/**
	 * 履歴キーを返却
	 * @return
	 */
	public Key getRevisionKey() {
		return getRevisionKey(revision);
	}

	/**
	 * 履歴キーを返却
	 * @return
	 */
	public Key getRevisionKey(int revision) {
		return getRevisionKey(getMyUri(), revision);
	}

	/**
	 * 履歴キーを返却
	 * @return
	 */
	public static Key getRevisionKey(String uri, int revision) {
		StringBuffer keyStr = new StringBuffer();
		keyStr.append(uri);
		keyStr.append(",");
		keyStr.append(revision);
		Key parentKey = getParentKey(uri);
		Key key = KeyFactory.createKey(parentKey, Entry.KIND, keyStr.toString());
		return key;
	}

	/**
	 * 本体のキーを返却
	 * Aliasから本体のキーを取得する場合に使用する
	 * keyが設定されている状態で実行すること
	 * @return
	 */
	public Key getBodyKey() {
		if (key == null) {
			return null;
		}
		Key parentKey = key.getParent();
		StringBuffer keyStr = new StringBuffer();
		keyStr.append(parentKey.getName());
		keyStr.append(",0");
		Key key = KeyFactory.createKey(parentKey, Entry.KIND, keyStr.toString());
		return key;
	}

	/**
	 * IDに設定する文字列を返却
	 * @return
	 */
	public String getOriginalId() {
		// ユニークな文字列（Encodedkey(parenturi, myuri, revision))
		// Keyを作成してから呼ぶこと
		StringBuffer originalId = new StringBuffer();
		String keyStr = key.getName();
		int idx = keyStr.lastIndexOf(",");
		if (idx > 0) {
			originalId.append(keyStr.substring(0, idx));
		} else {
			originalId.append(keyStr);
		}
		originalId.append(",");
		originalId.append(revision);
		return originalId.toString();
	}

	public void setOriginalId() {
		this.id = getOriginalId();
	}

	public String getMyUri() {
		String myUri = null;
		if (link != null) {
			for (Link childLink : link) {
				if (Link.REL_SELF.equals(childLink._$rel)) {
					myUri = childLink._$href;
				}
			}
		}
		return myUri;
	}

	public String getMyUriSlash() {
		String myUri = getMyUri();
		return editSlash(myUri);
	}

	public String getParentUri() {
		String myUri = getMyUriSlash();
		return getParentUri(myUri);
	}

	public String getSelfidUri() {
		String myUri = getMyUri();
		return getSelfidUri(myUri);
	}
	
	public void setKeyItem() {
		this.setParent(this.getParentUri());
		this.setSelfid(this.getSelfidUri());
		if (this.id != null) {
			this.setRevision(getRevisionFromId());
		}
		this.setDeleted("");
	}

	public void setMyUri(String uri) {
		String tmpUri = uri;
		// uriをparentとselfidに分割
		if (uri != null && uri.length() > 0) {
			if ("/".equals(uri)) {	// root layer
				this.setParent(null);
				this.setSelfid("/");
				
			} else {
				if ("/".equals(tmpUri.substring(tmpUri.length() - 1))) {
					tmpUri = tmpUri.substring(0, tmpUri.length() - 1);
				}
	
				int period = tmpUri.lastIndexOf(".");
				if (period != -1 && tmpUri.substring(period).indexOf("/") == -1) {
					tmpUri = tmpUri.substring(0, period);
				}
	
				int slash = tmpUri.lastIndexOf("/");
	
				if (slash == -1) {
					this.setParent("/");
					this.setSelfid(tmpUri);
				} else {
					this.setParent(tmpUri.substring(0, slash + 1));
					this.setSelfid(tmpUri.substring(slash + 1));
				}
			}

		} else {
			// root layer
			this.setParent(null);
			this.setSelfid("/");
			tmpUri = "/";
		}
		
		setLinkSelf(tmpUri);
	}
	
	public void setLinkSelf(String uri) {
		if (uri == null || uri.trim().length() == 0) {
			return;
		}
		if (link == null) {
			link = new ArrayList<Link>();
		}
		for (Link childLink : link) {
			if (Link.REL_SELF.equals(childLink._$rel)) {
				childLink._$href = uri;
				return;
			}
		}
		Link childLink = new Link();
		childLink._$rel = Link.REL_SELF;
		childLink._$href = uri;
		link.add(childLink);
	}

	public String getUriFromId() {
		return getUriFromId(this.id);
	}
	
	public static String getUriFromId(String id) {
		String url = null;
		if (id != null) {
			String[] temp = id.split(",");
			if (temp != null && temp.length >= 1) {
				url = temp[0];
			}
		}

		return url;
	}

	public int getRevisionFromId() {
		return getRevisionFromId(this.id);
	}

	public static int getRevisionFromId(String id) {
		int rev = 0;
		if (id != null) {
			String[] temp = id.split(",");
			try {
				if (temp != null && temp.length >= 2) {
					rev = Integer.parseInt(temp[1]);
				}
			} catch (Exception e) {}
		}

		return rev;
	}

	public void setRevisionFromId() {
		this.setRevision(this.getRevisionFromId());
	}
	
	/**
	 * URI(parent + selfid)を返却
	 * parentとselfidを設定後に実行してください。
	 * @return
	 */
	public String getUri() {
		if (parent == null && selfid == null) {
			return null;
		}
		StringBuffer buf = new StringBuffer();
		if (parent != null) {
			buf.append(parent);
		}
		if (selfid != null) {
			buf.append(selfid);
		}
		return buf.toString();
	}

	public static String editSlash(String myUri) {
		if (myUri != null) {
			if (myUri.lastIndexOf("/") < myUri.length() - 1 && myUri.indexOf(".") == -1) {
				myUri = myUri + "/";
			}
		} else {
			myUri = "/";
		}
		return myUri;
	}
	
	public static String removeLastSlash(String myUri) {
		String uri = editSlash(myUri);
		if (uri.length() > 1) {
			uri = uri.substring(0, uri.length() - 1);
		}
		return uri;
	}

	public static String getParentUri(String pMyUri) {
		String parentUri = null;
		String myUri = editSlash(pMyUri);
		if ("/".equals(myUri)) {
			//return "";	// root layer
			return null;	// root layer
		}
		if (myUri != null && myUri.length() > 1) {
			int index = myUri.lastIndexOf("/", myUri.length() - 2);
			if (index > -1) {
				parentUri = myUri.substring(0, index + 1);
			}
		}
		return parentUri;
	}

	public static String getSelfidUri(String pMyUri) {
		String selfidUri = null;
		String myUri = editSlash(pMyUri);
		if ("/".equals(myUri)) {
			return "/";	// root layer
		}
		myUri = myUri.substring(0, myUri.length() - 1);
		if (myUri != null) {
			int index = myUri.lastIndexOf("/");
			if (index > -1) {
				selfidUri = myUri.substring(index + 1);
			} else {
				selfidUri = myUri;
			}
		}
		return selfidUri;
	}
	
	/**
	 * categoryから値を取得します。
	 * @param term キー
	 * @return
	 */
	public String getProperty(String term) {
		if (term == null) {
			return null;
		}
		if (category == null) {
			return null;
		}
		for (Category prop : category) {
			if (term.equals(prop.get_$term())) {
				return prop.get_$label();
			}
		}
		return null;
	}
	
	/**
	 * categoryに値を設定します。
	 * @param term キー
	 * @param label 値
	 * @return
	 */
	public void setProperty(String term, String label) {
		if (term == null) {
			return;
		}
		if (category == null) {
			category = new ArrayList<Category>();
		}
		boolean hasTerm = false;
		for (Category prop : category) {
			if (term.equals(prop.get_$term())) {
				prop.set_$label(label);
				hasTerm = true;
			}
		}
		if (!hasTerm) {
			Category prop = new Category();
			prop.set_$term(term);
			prop.set_$label(label);
			category.add(prop);
		}
	}
	
	/**
	 * Aliasに指定されたURLを追加します。
	 * @param uri
	 */
	public void addAlias(String uri) {
		if (uri == null || uri.length() == 0) {
			return;
		}
		if (link == null) {
			link = new ArrayList<Link>();
		}
		
		boolean isExist = false;
		for (Link li : link) {
			if (Link.REL_ALTERNATE.equals(li.get_$rel()) && 
					uri.equals(li.get_$href())) {
				isExist = true;
			}
		}
		if (!isExist) {
			Link li = new Link();
			li.set_$rel(Link.REL_ALTERNATE);
			li.set_$href(uri);
			link.add(li);
		}
	}
	
	/**
	 * Aliasから指定されたURLを削除します。
	 * @param uri
	 */
	public void removeAlias(String uri) {
		if (uri == null || uri.length() == 0) {
			return;
		}
		if (link == null) {
			return;
		}
		
		for (int i = 0; i < link.size(); i++) {
			Link li = link.get(i);
			if (Link.REL_ALTERNATE.equals(li.get_$rel()) && 
					uri.equals(li.get_$href())) {
				link.remove(i);
				break;
			}
		}
	}
	
	/**
	 * Alias一覧を取得します。
	 * @return
	 */
	public List<String> getAlias() {
		if (link == null) {
			return null;
		}
		List<String> aliases = new ArrayList<String>();
		for (Link li : link) {
			if (Link.REL_ALTERNATE.equals(li.get_$rel())) {
				aliases.add(li.get_$href());
			}
		}
		if (aliases.size() == 0) {
			return null;
		}
		return aliases;
	}

	@Override
	public String toString() {
		return "Entry [_$xml$base=" + _$xml$base + ", _$xml$lang=" + _$xml$lang
				+ ", _$xmlns=" + _$xmlns + ", author=" + author + ", category="
				+ category + ", content=" + content + ", contributor="
				+ contributor + ", deleted=" + deleted + ", id=" + id
				+ ", id_$xml$base=" + id_$xml$base + ", id_$xml$lang="
				+ id_$xml$lang + ", isAlias=" + isAlias + ", selfid=" + selfid
				+ ", key=" + key + ", link=" + link + ", parent=" + parent
				+ ", published=" + published + ", published_$xml$base="
				+ published_$xml$base + ", published_$xml$lang="
				+ published_$xml$lang + ", revision=" + revision + ", rights="
				+ rights + ", rights_$type=" + rights_$type
				+ ", rights_$xml$base=" + rights_$xml$base
				+ ", rights_$xml$lang=" + rights_$xml$lang + ", source="
				+ source + ", summary=" + summary + ", summary_$type="
				+ summary_$type + ", summary_$xml$base=" + summary_$xml$base
				+ ", summary_$xml$lang=" + summary_$xml$lang + ", title="
				+ title + ", title_$type=" + title_$type + ", title_$xml$base="
				+ title_$xml$base + ", title_$xml$lang=" + title_$xml$lang
				+ ", updated=" + updated + ", updated_$xml$base="
				+ updated_$xml$base + ", updated_$xml$lang="
				+ updated_$xml$lang + "]";
	}

}
